package search.restapi;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Optional;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import common.config.IndexTypeNames;
import search.config.MediaTypes;
import search.config.SearchParameters;
import common.helper.json.JsonToJavaObject;
import search.model.query.ComplexParameter;
import search.service.manage.DatabaseDocumentService;
import search.service.manage.ElasticDocumentService;
import search.service.search.ComplexSearchService_V2;
import search.service.search.FullfieldSearchService;
import search.service.search.SimpleSearchService_V1;
import search.service.search.SimpleSearchService_V2;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.io.IOException;

@Api(value="search", description="Search Rest Api")
@Path("/v1/search")
@Produces(MediaTypes.JSON_UTF8)
@Component
public class SearchRestAPI {
    final Logger logger = LoggerFactory.getLogger(SearchRestAPI.class);

    private final ObjectMapper mapper;

    private final ElasticDocumentService elasticDocumentService;
    private final DatabaseDocumentService databaseDocumentService;
    private final SimpleSearchService_V1 searchService;
    private final FullfieldSearchService fullfieldSearchService;
    private final SimpleSearchService_V2 simpleSearchService;
    private final ComplexSearchService_V2 complexSearchService;

    @Inject
    public SearchRestAPI(
            ObjectMapper mapper,
            ElasticDocumentService elasticDocumentService,
            DatabaseDocumentService databaseDocumentService,
            SimpleSearchService_V1 searchService,
            FullfieldSearchService fullfieldSearchService,
            SimpleSearchService_V2 simpleSearchService,
            ComplexSearchService_V2 complexSearchService
    ) {
        this.mapper = mapper;
        this.elasticDocumentService = elasticDocumentService;
        this.databaseDocumentService = databaseDocumentService;
        this.searchService = searchService;
        this.complexSearchService = complexSearchService;
        this.fullfieldSearchService = fullfieldSearchService;
        this.simpleSearchService = simpleSearchService;
    }

    @PostConstruct
    private void init() {
        logger.info("Initialized /v1/search/ Successfully!");
    }



    @ApiOperation(value="_byid")
    @GET
    @Path("/_byid/{level}/{id}")
    @Produces(MediaTypes.JSON_UTF8)
    public String get(
            @ApiParam(value="level",required=true)
            @PathParam("level") String level ,
            @ApiParam(value="id",required=true)
            @PathParam("id") String id) {

        return Optional.fromNullable(elasticDocumentService.get(level,id)).or("{}");
    }




    @POST
    @Path("/_complex_search")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaTypes.JSON_UTF8)
    public String complexSearchPost(ComplexParameter adpara){
        logger.info("[complexSearchPost] params = \n{}", adpara.toString());
        return complexSearchService.complexSearch(adpara);
    }





    @GET
    @Path("/_complex_search")
    @Produces(MediaTypes.JSON_UTF8)
    public String complexSearchGet(
                         @QueryParam("from")  @DefaultValue("0") Integer from,
                         @QueryParam("size") @DefaultValue("10") Integer size,
                         @QueryParam("fulltext") String fulltext,
                         @QueryParam("pinyin") String pinyin,
                         @QueryParam("level") String level,
                         @QueryParam("levels") String levels,
                         @QueryParam("minimum") String minimum,
                         @QueryParam("mediatype") String mediatype,
                         @QueryParam("sorttype") String sorttype,
                         @QueryParam("source") String source,
                         @QueryParam("mediaform") String mediaform,
                         @QueryParam("subject") String subject,
                         @QueryParam("region") String region,
                         @QueryParam("mediasize") String mediasize,
                         @QueryParam("format") String format,
                         @QueryParam("copyright") String copyright,
                         @QueryParam("shotdatefrom") String shotdatefrom,
                         @QueryParam("shotdateto") String shotdateto,
                         @QueryParam("explain") String explain,
                         @QueryParam("dsl") String dsl
    ){
        //Optional<String> new_dsl = Optional.fromNullable("false");
        return complex_search(
                this.logger, this.mapper, this.complexSearchService,
                Optional.fromNullable(from), Optional.fromNullable(size), Optional.fromNullable(fulltext), Optional.fromNullable(pinyin),
                Optional.fromNullable(level), Optional.fromNullable(minimum), Optional.fromNullable(mediatype), Optional.fromNullable(sorttype),
                Optional.fromNullable(source), Optional.fromNullable(mediaform),
                Optional.fromNullable(subject), Optional.fromNullable(region), Optional.fromNullable(mediasize),
                Optional.fromNullable(format), Optional.fromNullable(copyright), Optional.fromNullable(shotdatefrom), Optional.fromNullable(shotdateto),
                Optional.fromNullable(explain), Optional.fromNullable(dsl)
        );
    }


    /******************************************
     *
     * @param from
     * @param size
     * @param fulltext
     * @param pinyin
     * @param level
     * @param minimum
     * @param mediatype
     * @param sorttype
     * @param source
     * @param mediaform
     * @param subject
     * @param region
     * @param mediasize
     * @param format
     * @param copyright
     * @param shotdatefrom
     * @param shotdateto
     * @param explain
     * @param dsl
     * @return
     */
    public static String complex_search(
            Logger logger,
            ObjectMapper mapper,
            ComplexSearchService_V2 complexSearchService,
            Optional<Integer> from,
            Optional<Integer> size,
            Optional<String> fulltext,
            Optional<String> pinyin,
            Optional<String> level,
            Optional<String> minimum,
            Optional<String> mediatype,
            Optional<String> sorttype,
            Optional<String> source,
            Optional<String> mediaform,
            Optional<String> subject,
            Optional<String> region,
            Optional<String> mediasize,
            Optional<String> format,
            Optional<String> copyright,
            Optional<String> shotdatefrom,
            Optional<String> shotdateto,
            Optional<String> explain,
            Optional<String> dsl

    )
    {
        ObjectNode rootNode = mapper.createObjectNode();
        ((ObjectNode)rootNode).put("from" , from.or(0));
        ((ObjectNode)rootNode).put("size" , size.or(10));
        ((ObjectNode)rootNode).put("fulltext" , fulltext.or(""));
        ((ObjectNode)rootNode).put("pinyin" , pinyin.or(""));
        ArrayNode arrNode = mapper.createArrayNode();
        if(level.isPresent())
            ((ArrayNode)arrNode).add(level.or( IndexTypeNames.MediaDB.FileSetName ));
        ((ObjectNode)rootNode).put("levels", arrNode);
        ((ObjectNode)rootNode).put("minimum" , minimum.or(SearchParameters.MinimumMatch.ori_minimum));
        ((ObjectNode)rootNode).put("mediatype" , mediatype.orNull());
        ((ObjectNode)rootNode).put("sorttype" , sorttype.orNull());
        ((ObjectNode)rootNode).put("source" , source.orNull());
        ((ObjectNode)rootNode).put("mediaform" , mediaform.orNull());
        ((ObjectNode)rootNode).put("subject" , subject.orNull());
        ((ObjectNode)rootNode).put("region" , region.orNull());
        ((ObjectNode)rootNode).put("mediasize" , mediasize.orNull());
        ((ObjectNode)rootNode).put("format" , format.orNull());
        ((ObjectNode)rootNode).put("copyright" , copyright.orNull());
        ((ObjectNode)rootNode).put("shotdatefrom", shotdatefrom.orNull());
        ((ObjectNode)rootNode).put("shotdateto" , shotdateto.orNull());
        rootNode.put("explain", explain.or("false"));
        rootNode.put("dsl", dsl.or("false"));

        String json = JsonToJavaObject.jsonNodeToString(rootNode);
        logger.debug("complex search by get \n{}", json);
        ComplexParameter params = new ComplexParameter();
        try {
            params = mapper.readValue(json, ComplexParameter.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return complexSearchService.complexSearch(params);
    }




    @GET
    @Path("/_search_old_v1")
    @Produces(MediaTypes.JSON_UTF8)
    public String search(
            @QueryParam("q") String q,
            @QueryParam("from") @DefaultValue("0") Integer from,
            @QueryParam("size") @DefaultValue("10") Integer size,
            @QueryParam("mediatype") String mediatype,
            @QueryParam("sorttype") String sorttype,
            @QueryParam("explain")  String explain,
            @QueryParam("dsl") String dsl
    ) {

        Optional<String> oq = Optional.fromNullable(q);
        Optional<Integer> ofrom = Optional.fromNullable(from);
        Optional<Integer> osize = Optional.fromNullable(size);
        Optional<String> omediatype = Optional.fromNullable(mediatype);
        Optional<String> osorttype  = Optional.fromNullable(sorttype);
        Optional<String> oexplain = Optional.fromNullable(explain);
        Optional<String> odsl = Optional.fromNullable(dsl);

        return searchService.search(
                oq.orNull(),
                ofrom.or(0),
                osize.or(10),
                omediatype.or("video"),
                osorttype.or("relevancy"),
                oexplain.or(""),
                odsl.or("false")
        );
    }





    @GET
    @Path("/_search")
    @Produces(MediaTypes.JSON_UTF8)
    public String simple_search(
            @QueryParam("q") String q,
            @QueryParam("from") @DefaultValue("0") Integer from,
            @QueryParam("size") @DefaultValue("10") Integer size,
            @QueryParam("mediatype") String mediatype,
            @QueryParam("sorttype") String sorttype,
            @QueryParam("minimum") String minimum,
            @QueryParam("explain")  String explain,
            @QueryParam("dsl") String dsl,
            @QueryParam("level") String level
        ) {
        Optional<String> oq = Optional.fromNullable(q);
        Optional<Integer> ofrom = Optional.fromNullable(from);
        Optional<Integer> osize = Optional.fromNullable(size);
        Optional<String> omediatype = Optional.fromNullable(mediatype);
        Optional<String> osorttype  = Optional.fromNullable(sorttype);
        Optional<String> ominimum = Optional.fromNullable(minimum);
        Optional<String> oexplain = Optional.fromNullable(explain);
        Optional<String> odsl = Optional.fromNullable(dsl);
        Optional<String> olevel = Optional.fromNullable(level);
        //String dsl = "false";
        logger.debug("GET /search/_search");
        return simpleSearchService.simpleSearch(
                oq.orNull(),
                ofrom.or(0),
                osize.or(10),
                omediatype.or("video"),
                osorttype.or("relevancy"),
                ominimum.or(SearchParameters.MinimumMatch.default_minimum),
                oexplain.or(""),
                odsl.or("false"),
                olevel.or("")
        );
    }





    /********************************************
     *
     * @param args
     */
    public static void main(String[] args){

    }
}

